#ifndef __COMPONENT_MODULE_H__
#define __COMPONENT_MODULE_H__

#include "ibase_impl.h"

namespace Boss
{

  namespace Internal
  {

    template <typename T>
    struct ObjectCreator
    {
      typedef RefObjPtr<IBase> IBasePtr;
      static IBasePtr Create(TGuid coClassId)
      {
        typedef typename T::Head CurType;
        if (IsEqualGUID(coClassId, BOSS_CLSID_OF(CurType)))
        {
          typedef IBaseImpl<CurType> ImplType;
          typedef RefObjPtr<ImplType> ImplTypePtr;
          ImplTypePtr NewInst(ImplType::Create());
          IBasePtr Base;
          if (NewInst.QueryInterface(Base.GetPPtr()) != rcOk)
            return IBasePtr(0);
          return Base;
        }
        return ObjectCreator<typename T::Tail>::Create(coClassId);
      }
    };

    template <>
    struct ObjectCreator<TL::NullType>
    {
      static RefObjPtr<IBase> Create(TGuid)
      {
        return RefObjPtr<IBase>(0);
      }
    };

    class ModuleEnviroment
      : private NonCopyable
    {
    public:
      ModuleEnviroment()
        : SynObj(CreateSynObject())
      {
      }
      RefObjPtr<IBase> Get() const
      {
        ISyncObject Lock(*SynObj.get());
        return Enviroment;
      }
      void Set(RefObjPtr<IBase> enviroment)
      {
        ISyncObject Lock(*SynObj.get());
        Enviroment = enviroment;
      }

    private:
      mutable std::auto_ptr<ISynObj> SynObj;
      RefObjPtr<IBase> Enviroment;
    };

    template <typename Y>
    struct CoClassIdExtractor
    {
      static TGuid GetId(unsigned index, unsigned curIndex)
      {
        if (index == curIndex)
        {
          typedef typename Y::Head CurType;
          return BOSS_CLSID_OF(CurType);
        }
        return CoClassIdExtractor<typename Y::Tail>::GetId(index, curIndex + 1);
      }
    };

    template <>
    struct CoClassIdExtractor<TL::NullType>
    {
      static TGuid GetId(unsigned, unsigned)
      {
        return 0;
      }
    };

    template <typename TCoClasses>
    class Module
      : private NonCopyable
    {
    public:
      static ULong GetCoClassCount()
      {
        return static_cast<ULong>(TL::ListLength<TCoClasses>::Length);
      }
      static TGuid GetCoClassId(unsigned index)
      {
        return CoClassIdExtractor<TCoClasses>::GetId(index, 0);
      }
      static ULong GetRefCount()
      {
        return ModuleCounter<>::GetInst().GetCounter();
      }
      static RefObjPtr<IBase> CreateObject(TGuid classId)
      {
        return ObjectCreator<TCoClasses>::Create(classId);
      }

    private:
      Module();
    };

  }

}

#ifdef _MSC_VER
#define BOSS_EXPORT_FUNCTION extern "C" __declspec(dllexport)
#else
#define BOSS_EXPORT_FUNCTION extern "C"
#endif

#define BOSS_MODULE_INIT(module_syn_obj) \
  namespace Boss \
  { \
    namespace Internal \
    { \
      namespace \
      { \
        ULong StartModuleCounter = ModuleCounter<>::GetInst().GetCounter(); \
      } \
      std::auto_ptr<ISynObj> CreateSynObject() \
      { \
        return std::auto_ptr<ISynObj>(new ISynObjImpl<module_syn_obj>); \
      } \
      ModuleEnviroment& GetModuleEnviroment() \
      { \
        static ModuleEnviroment Inst; \
        return Inst; \
      } \
    } \
    RefObjPtr<IBase> GetEnviroment() \
    { \
      return Internal::GetModuleEnviroment().Get(); \
    } \
  }

#define BOSS_DECLARE_MODULE_ENTRY_POINT(module_name, module_guid, module_syn_obj, module_coclasslist) \
  namespace Boss \
  { \
    namespace Internal \
    { \
      typedef Module<module_coclasslist> ModuleType; \
    } \
  } \
  BOSS_MODULE_INIT(module_syn_obj) \
  BOSS_EXPORT_FUNCTION void SetEnviroment(Boss::TPtr enviroment) \
  { \
    Boss::Internal::GetModuleEnviroment().Set(Boss::RefObjPtr<Boss::IBase>(reinterpret_cast<Boss::IBase *>(enviroment))); \
  } \
  BOSS_EXPORT_FUNCTION const char* GetModuleName() \
  { \
    return module_name; \
  } \
  BOSS_EXPORT_FUNCTION Boss::TGuid GetModuleGuid() \
  { \
    return #module_guid; \
  } \
  BOSS_EXPORT_FUNCTION unsigned GetCoClassCount() \
  { \
    return Boss::Internal::ModuleType::GetCoClassCount(); \
  } \
  BOSS_EXPORT_FUNCTION Boss::TGuid GetCoClassId(unsigned index) \
  { \
    return Boss::Internal::ModuleType::GetCoClassId(index); \
  } \
  BOSS_EXPORT_FUNCTION Boss::ULong CreateObject(Boss::TGuid classId, Boss::TPtr *iface) \
  { \
    try \
    { \
      Boss::RefObjPtr<Boss::IBase> Obj(Boss::Internal::ModuleType::CreateObject(classId)); \
      if (!Obj.Get()) \
        return Boss::rcFail; \
      return Obj->QueryInterface(BOSS_UUID_OF(Boss::IBase), iface); \
    } \
    catch (std::exception &) \
    { \
      return Boss::rcFail; \
    } \
  } \
  BOSS_EXPORT_FUNCTION Boss::ULong GetModuleCounter() \
  { \
    return Boss::Internal::ModuleType::GetRefCount(); \
  }

#endif  // !__COMPONENT_MODULE_H__
